"""
https://blog.csdn.net/lj2048/article/details/78547198
"""

import numpy as np
import operator
import os
import sys
sys.path.append("..")  # 为了导入父目录的文件而进行的设定
import matplotlib
import matplotlib.pyplot as plt

def classify0(test_data, train_data, train_labels, k):
    train_data_length = train_data.shape[0]
    diffMat = np.tile(test_data, (train_data_length, 1)) - train_data
    sqDiffMat = diffMat ** 2
    sqDistance = sqDiffMat.sum(axis=1)
    distance = sqDistance ** 0.5
    sortDistance = distance.argsort()
    classCount = {}
    for i in range(k):
        voteLabel = train_labels[sortDistance[i]]
        classCount[voteLabel] = classCount.get(voteLabel, 0) + 1
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]


def img2Vector(filename):
    """
    #图像转换函数（32*32图像转换为1*1024向量）
    """
    returnVector = np.zeros((1,1024)) # 创建一个零矩阵
    fr = open(filename)
    for i in range(32):
        lineStr = fr.readline() # 每次读取一行内容，以字符串形式存储
        # 逐个取出当前行的每一个字符，并转化为数字
        # returnVector为numpy数据类型，所以比如returnVector[0,15]代表的是输出第0行第15列的数字
        for j in range(32):
            returnVector[0, 32*i+j] = int(lineStr[j])
    fr.close()
    return returnVector


def classTest():
    """
    手写数字识别测试函数
    classTest()函数用于处理32*32的数据，
    """
    Labels = [] # 初始化类别列表为空列表

    # 对训练数据进行处理
    traingFileList = os.listdir("E:\Pythonyuanchengxu\KNN\digits\\trainingDigits") #listdir函数用列出给定目录下所有训练数据的文件名,并且以数组的方式存储
    length = len(traingFileList) #获取训练数据数目的数组长度
    trainingMat = np.zeros((length, 1024)) #声明一个length*1024的矩阵用于存储所有样本的向量形式
    # 遍历每一个训练数据
    for i in range(length):
        fileNameStr = traingFileList[i] # 获取列表中每一个文件名（包括后缀）
        fileName = fileNameStr.split(".")[0] # 去掉文件名的后缀
        numClass = fileName.split("_")[0] #获取该文件所属的类别（因为文件名都是以‘数字类别_第几个样本.txt’形式的)
        # 将代表该训练数据类别的数字存入类别标签列表
        Labels.append(numClass)
        # 调用图像转换函数将该训练数据的输入特征转换为向量并存储
        trainingMat[i, :] = img2Vector("E:\Pythonyuanchengxu\KNN\digits\\trainingDigits\\" + fileNameStr)

     # 对测试数据进行处理 ，列出给定目录下所有测试数据的文件名
    testFileList = os.listdir("E:\Pythonyuanchengxu\KNN\digits\\testDigits")
    lengthTest = len(testFileList)
    errorCount = 0.0
    # 遍历每一个测试数据
    for i in range(lengthTest):
        fileNameStr = testFileList[i]
        fileName = fileNameStr.split(".")[0]
        numClass = fileName.split("_")[0]
        # 调用图像转换函数将该测试数据的输入特征转换为向量
        vectorUnderTest = img2Vector("E:\Pythonyuanchengxu\KNN\digits\\testDigits\\" + fileNameStr)
        # 调用k-NN简单实现函数，并返回分类器对该测试数据的分类结果
        classifierResult = classify0(vectorUnderTest, trainingMat, Labels, 3)
        print("the classifier came back with: %d, the real answer is: %d" % (int(classifierResult), int(numClass)))
        # 统计分类器对测试数据分类犯错的个数
        if classifierResult != numClass:
            errorCount += 1
    print("\nthe total number of error is: %d" % errorCount)
    # 输出分类器错误率  
    print("\nthe total error rate is: %f" % (errorCount / float(lengthTest)))


def main():
    map = img2Vector("E:\Pythonyuanchengxu\KNN\digits\\trainingDigits\\0_1.txt") # 注意文件名中需要使用“\\”作为转义字符
    print(map[0, 15])
    classTest()


if __name__ == '__main__':
    main()